#![allow(dead_code)]

/// NOTE: AsRef 与 Borrow，都可实现 T → &U 的操作，一般情况下使用 AsRef 即可，除非 T 要作为 Map 中的键，在实现 `Borrow<U>` 时，U 还需要实现 Hash、Eq，这样 &U 也可以作为键使用。
struct BorrowLrn {}

impl BorrowLrn {
    pub fn hello() {
        println!("Borrow learning!");
    }
}

#[cfg(test)]
mod tests {
    use std::borrow::Borrow;
    use std::convert::AsRef;

    #[test]
    #[allow(noop_method_call)]
    #[allow(clippy::needless_borrow)]
    fn default_borrow() {
        // NOTE: 引入Borrow trait
        // impl Borrow<U> for T
        // T → &U
        use std::borrow::Borrow;

        struct MyType;

        let my_type = MyType;
        let _a = my_type.borrow();
        let _b = (&my_type).borrow();

        // NOTE: as_ref() 实现的是 primitive types, 不能用于自定义类型. borrow() 可以用于自定义类型, 也许不对.

        // 以下报错
        // let _c: &MyType = &my_type.as_ref();

        let my_type = &MyType;
        let _a = my_type.borrow();
        let _b = (*my_type).borrow();

        let my_type = &mut MyType;
        let _: &MyType = my_type.borrow();
        let _: &MyType = (*my_type).borrow();
    }

    #[test]
    /// NOTE: borrow_mut() 类似 as_mut()
    fn borrow_mut_test() {
        let mut v = vec![1, 2, 3];
        // as_mut()
        let _a: &mut [i32] = v.as_mut();
        //
        // check() 可变借用v
        check_v1(&mut v);
        assert_eq!(v, [4, 13, 3]);
        check_v2(&mut v, 14);
        assert_eq!(v, [14, 13, 3]);
        check_v3(&mut v);
        assert_eq!(v, [5, 14, 3]);

        // T 实现BorrowMut, v为可变借用T
        fn check_v1<T: std::borrow::BorrowMut<[i32]>>(v: &mut T) {
            assert_eq!(&mut [1, 2, 3], v.borrow_mut());
            v.borrow_mut()[0] = 4;
            v.borrow_mut()[1] = 13;
        }

        // 如果改用泛型, 需考虑泛型T的一致性
        fn check_v2<T>(v: &mut [T], var: T) {
            v[0] = var;
        }

        // 先实现std::convert::AsMut<[i32]>, 才能用as_mut()
        fn check_v3<T: std::convert::AsMut<[i32]>>(v: &mut T) {
            assert_eq!(&mut [14, 13, 3], v.as_mut());
            v.as_mut()[0] = 5;
            v.as_mut()[1] = 14;
        }
    }

    #[test]
    /// NOTE: 从拥有的值中一成不变的借用
    /// 正常不可变借用traits, 必须声明traits，才能使用
    fn borrow_test() {
        let v = vec![1, 2, 3];
        let o = [1, 2, 3];
        check_v1(&v);
        check_v1a(&v);
        check_v2(&v);
        check_v3(&v);
        check_v4(&v, &o);

        /// 输入T:实现Borrow trait<i32>数组, 比较数组
        fn check_v1<T: std::borrow::Borrow<[i32]>>(v: &T) {
            assert_eq!(&vec![1, 2, 3], v.borrow());
            assert_eq!(&[1, 2, 3], v.borrow());
        }

        /// 输入T:实现AsRef trait<i32>数组, 比较数组
        fn check_v1a<T: AsRef<[i32]>>(v: &T) {
            assert_eq!(&vec![1, 2, 3], v.as_ref());
            assert_eq!(&[1, 2, 3], v.as_ref());
            // println!("{:?}", v.as_ref());
        }

        /// 输入Vec<i32>数组，比较Vec<i32>数组
        fn check_v2(v: &Vec<i32>) {
            // println!("{:?}", v);
            // print_type(&v);
            assert_eq!(&vec![1, 2, 3], v);
        }

        /// 输入泛型数组，比较[i32]
        fn check_v3<T>(v: &[T])
        where
            T: std::fmt::Debug + std::cmp::PartialEq,
            i32: PartialEq<T>,
        {
            // NOTE: assert_eq!宏需要实现Debug traits
            assert_eq!(&vec![1, 2, 3], v);
        }

        // NOTE: 等式关系 == 必须满足以下条件 (对于所有类型为 A、B、C 的 a、b、c) ：
        // 对称: 如果 A: PartialEq<B> 和 B: PartialEq<A>，则 a == b 意味着 ’b == a`; 和
        // 可传递: 如果 A: PartialEq<B> 和 B: PartialEq<C> 以及 A： PartialEq<C>，然后 a == b，并且 b == c 暗示了 a == c。
        // 请注意，B: PartialEq<A> (symmetric) 和 A: PartialEq<C> (transitive) 强制不是强制存在的，但是这些要求只要存在就适用。
        fn check_v4<T, N>(v: &[T], o: &[N])
        where
            T: std::fmt::Debug + PartialEq<N>,
            N: std::fmt::Debug + PartialEq<T>,
        {
            assert_eq!(v, o);
        }

        // NOTE: 打印变量，类型，及大小函数
        fn print_type<T: std::fmt::Debug>(value: &T) {
            println!(
                "{:?} ({}, {} bytes)",
                *value,
                std::any::type_name::<T>(),
                std::mem::size_of::<T>()
            );
        }
    }

    #[test]
    fn as_ref_test() {
        let str1 = "hello";
        is_hello(str1);

        let str2 = "hello".to_string();
        is_hello(str2);

        fn is_hello<T: AsRef<str>>(s: T) {
            assert_eq!("hello", s.as_ref());
        }
    }

    #[test]
    /// NOTE: to_owned()需要实现Copy, Clone traits
    /// 从借用的数据创建拥有的数据，通常是通过克隆。
    fn to_owned_test() {
        #[derive(Debug, Copy, Clone)]
        struct MyType(i32);

        let mut v = vec![1, 2, 3];
        let x = v.to_owned();
        v.push(4);
        assert_eq!(x, [1, 2, 3]);

        let my_type = MyType(788);
        let y = my_type.to_owned();
        dbg!(y);
    }

    #[test]
    fn clone_into_test() {
        let v = vec![1, 2, 3];
        let mut x = Vec::new();
        v.clone_into(&mut x);
        assert_eq!(v, x);
    }

    #[test]
    fn only_borrow_test() {
        let x = vec![1, 2, 3];
        // NOTE: as_ref() 可以直接用，borrow()需要声明 std::borrow::Borrow;
        let y: &[i32] = x.as_ref();
        let z: &[i32] = x.borrow();
        assert_eq!(y, &[1, 2, 3]);
        assert_eq!(z, &[1, 2, 3]);
    }

    #[test]
    fn hello_spc() {
        println!("hello spc!");
    }
}
